package com.wxhandle.cleandemo.api.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

// import lombok.extern.slf4j.Slf4j;

// @Slf4j
public class DefaultInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource{

    private Map<String, Set<RequestHandlerMapping>> caMapping;

    private void loadPatterns(HttpServletRequest request){

        ServletContext sc = request.getSession().getServletContext();
        WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
        RequestMappingHandlerMapping mapping = ac.getBean(RequestMappingHandlerMapping.class);

        // 拿到Handler适配器中的全部方法
        Map<RequestMappingInfo, HandlerMethod> methodMap = mapping.getHandlerMethods();
        caMapping = new HashMap<>(); 
        Iterator<RequestMappingInfo> it = methodMap.keySet().iterator();
        while(it.hasNext()){
            RequestMappingInfo mi = it.next();
            HandlerMethod method = methodMap.get(mi);
            if(method.getBeanType().getPackageName().startsWith("com.wxhandle.cleandemo.api.controller")){
                
                String controller = method.getBean().toString().toLowerCase();
                controller = controller.substring(0, controller.length() - 10);
                String handler = controller + "/" + method.getMethod().getName();
                Iterator<String> iurl = mi.getPatternsCondition().getPatterns().iterator();
                Iterator<RequestMethod> imethod = mi.getMethodsCondition().getMethods().iterator();
                while(iurl.hasNext() && imethod.hasNext()) {
                    String url= iurl.next();
                    String[] segments = url.split("/");
                    if(segments.length > 2) {
                        if(!caMapping.containsKey(segments[2]))
                            caMapping.put(segments[2], new HashSet<>());
                        caMapping.get(segments[2]).add(RequestHandlerMapping.of(imethod.next().name(), url, handler));
                    }
                }
            }
        }
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        HttpServletRequest request = ((FilterInvocation) object).getRequest();

        if(null == caMapping)
            loadPatterns(request);

        String[] urls = request.getRequestURI().split("/"); 
        if(urls.length > 2) {
            Iterator<RequestHandlerMapping> iterator = caMapping.get(urls[2]).iterator();
            Collection<ConfigAttribute> attributes = new ArrayList<>();
            while(iterator.hasNext()){
                RequestHandlerMapping map = iterator.next();
                String method =request.getMethod();
                if(map.getMethod().equals(method)) {
                    AntPathRequestMatcher matcher = new AntPathRequestMatcher(map.getPattern());
                    if (matcher.matches(request)) {
                        attributes.add(new SecurityConfig(map.getHandler()));
                        return attributes;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
    
}
